<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0, user-scalable=0">
  <meta http-equiv="Expires" content="0">
  <meta http-equiv="Pragma" content="no-cache">
  <meta http-equiv="Cache-control" content="no-cache">
  <meta http-equiv="Cache" content="no-cache">
  <link rel="icon" href="data:;base64,=">
  <title>textContext文本解析</title>
  </head>
  <style>
  *{margin:0 auto;padding:0;box-sizing:border-box;}
  html,body{position:absolute;width:100%;height:100%;min-width:1366px;min-height:768px;background: linear-gradient(skyblue,khaki);}
  /* 定义滚动条宽度 */::-webkit-scrollbar {width:4px;height:4px;}
  /*定义滚动条  轨道背景色+圆角*/::-webkit-scrollbar-track {border-radius: 2px;background-color: #555;}
  /*定义滚动条  滑块背景色+圆角*/::-webkit-scrollbar-thumb {border-radius: 2px;background-color: #C3C4C3;}
  /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  
  
  </style>
<body>


</body>
</html>
<script>
var log=console.log;
//~~~

// var regFun=/\w+\s*\(.*\)$/;                  //用于匹配执行方法
var regFun=/^([A-z]|\_|\$)+\d*\s*\(.*\)$/;      //以 字母_或$ 开头的 方法   
var regSm=/([^\?\!\=\:\>\<\+\-\*\/\(\)]+)/; // 匹配三目运算符中   不包含 +-*/ ? : > < = () 的参数  ,(没有排除掉 '"abc"' 另外排除)
// var regSm=/[\?\!\=\:\>\<\+\-\*\/\(\)]/;         // 对运算符进行分割        
var regCs=/^([A-z]|\_|\$)+/;                    // 以字母 _ 和 $ 开头的 肯定是 参数名  (  排除掉 '"abc"'  这种 )
var regKh=/(\{\{[^{}]+\}\})/g;                  // 括号内的内容  
var regSc=/({{|}})/g;                           //去除两边的括号
var regFg=/,(?![^(]*\))/g                       // 以 , 号 和 方法进行分割  ，这个正则表达式的含义是匹配逗号,，但是不匹配后面跟着任意数量（零次或多次）的不包含左括号(的字符，再跟着一个右括号)的子串。这样就可以排除括号内部的逗号。    
var regStr=/("|'|`)\w+\1/g;                     // 用于将所有的 'xx' , "xx" , `xx` 字符串提取出来   

/* 
  对每个 key 进行初步解析, 提取出所有参数
*/     
var jxKeyCl=function(key){
        // 提取出所有字符串
    var srr=key.match(regStr),i=0,l=0,f=false;      
        if(srr!==null){
            l=srr.length;
            while(i<l){
               key=key.replace(srr[i],'★☆☎_'+i);
               i++;
            };
            f=true;  //需要替换
        };
       // 对 所有 
    var crr=key.split(regSm),k='';  
        i=0,l=crr.length;
        while(i<l){
            k=crr[i].trim();
            if(regCs.test(k)){
               crr[i]=jxKeyPath(k,)
            };
          i++;
        }
    var str=crr.join('');    
        if(f){
           i=0,l=srr.length;
           while(i<l){
               str=str.replace('★☆☎_'+i,srr[i])
               i++;
           };
        };
        return str;
};  

var jxKeyPath=function(key){
     if(key.indexOf('.')<0){
             //直接key
             return '$data.'+key;
      }else{ //路径key
          var krr=key.split('.'),n=krr.length-1;
          var k=krr[0];
              krr[0]='$O.'+k;
              k=krr[n];
              krr[n]='$data.'+k;   
              return krr.join('.');
      };
}
/*
  解析 方法 文本   fun( a,b,c, d( a,b,c,))
*/
var jxStrFun=function(str){
    let n=str.indexOf('(');                         //
    let name=str.slice(0,n).trim();                 // 方法名
    let argStr=str.slice(n+1,str.length-1).trim();  // 内参
       if(argStr!==''){
           let args=argStr.split(regFg),i=0,l=args.length,arg='';
                  while(i<l){
                        arg=args[i].trim();
                        if(regFun.test(arg)){       //是个方法 , 需要二次解析
                            args[i]=jxStrFun(arg)
                        }else{                      // 直接参数 或 运算参数
                            args[i]=jxKeyCl(arg)
                        };
                    i++;
               };  
             return '$methods.'+name+'('+args.join(',')+')'   
       };
     return '$methods.'+name+'()';
};    




//文本
var str='测试1 文本内容 txt0= {{txt0}}; txt1={{txt1}} ; 三目运算=> {{ txt0>txt2?"haha":txt3+"vv" }}   test(txt0, txt1 , txt2 ) = {{ test(txt0, txt1 , txt2 ,"常量", test2( txt3,"{测试1}{{测试2}}" ) ) }} ;  txt0+123={{txt0+123+"哈哈"}}  路径值={{base.base2.txt0}} '

var vrr=str.split(regKh);

var i=0,l=vrr.length,k='',s='';
var fkk=[];
     l=0;
     while(i<l){
          k=vrr[i];
          if(regKh.test(k)){ // 在{{}} 内的值
               log(k); 
               s=k.slice(2,k.length-2).trim();    //去除两边括号 => 去除两边空格
               log(s);
               if(regFun.test(s)){
                    log('方法  \n ');
                     
                     vrr[i]= jxStrFun(s);

               }else{
                    log('%c字符串 \n ','color:red')

                    vrr[i]=jxKeyCl(s);
               };
               fkk.push(i);
          };

       i++;
     };

  //  console.log(vrr)





// reg=/(\{\{((?:[^{}]|\{[^{}]*\})*)\}\})/g

// 匹配 {{xxx }}
var defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g
// 匹配所有运算符 
var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g
// strip strings in expressions
var stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g;


var regKh=/\{\{((?:.|\n)+?)\}\}/g
/* 


    / 开始和结束正则表达式
    {{ 匹配左花括号 { ，因为 { 是元字符，所以要用反斜杠转义
    ((?:.|\n)+?) 匹配花括号里面的内容，是一个捕获组
    (?:.|\n) 匹配任意字符或换行符，是一个非捕获组，不会保存匹配结果
    +? 匹配前面的非捕获组一次或多次，但是尽可能少地匹配，这是一个惰性量词
    }} 匹配右花括号 } ，同样要用反斜杠转义
    g 是一个修饰符，表示全局匹配，即匹配所有符合条件的结果

*/

var matchKey=function(str){
    var arr=str.match(regKh);
        if(arr){
            var srr=[],s=0,n=0;
            var i=0,l=arr.length,v='',m=0;
            var nrr=[];   //拼接位置
                while(i<l){
                    v=arr[i]
                    n=str.indexOf(v);
                    if(n-s>0)srr.push(str.slice(s,n));  // 改key 之前的数据  
                    m=v.length;
                    s=n+m;              // 下个key的起点 
                    n=srr.push(str.slice(n,s));
                    nrr.push(n-1);     // 该key所在的拼接索引 
                  i++;
                };
                arr=null;
                return [srr,nrr]
        };
        return null;  
};

// matchKey(str);
// regFun0(str)

var getVfun=function(str){

    return Function('"use strict";return (' + str + ')')() 
};


fun= getVfun('function(data){ var v=data.v; return v }')

var data={txt0:'haha',$data:{txt0:'haha',txt:'1122'}};

var str=`
    function(o){
        var $data=o.$data;
        return "\n                  循环数据 "+$data.txt
    }
`;
f=eval('('+str+')')


[0,1, 0,1 ]


</script>